/*
 * @Author: dadadaXU 1413107032@qq.com
 * @Date: 2024-10-16 15:50:50
 * @LastEditors: dadadaXU 1413107032@qq.com
 * @LastEditTime: 2025-01-04 11:47:09
 * @FilePath: /FileBackup/include/fileBackupUnit.hpp
 * @Description: `FileBaseUnit` 基础公有函数
 */

#include "../include/fileBaseUnit.hpp"

FileBaseUnit::FileBaseUnit(const std::filesystem::path &file_path)
{
    /* 解析文件元数据 */
    if (lstat(file_path.c_str(), &(this->fileHeader.metadata)) != 0)
    {
        std::cerr << "Error: Unable to get file status - " << file_path << std::endl;
        return;
    }

    FileType file_type = this->GetFileType();
    if (file_type & FILE_TYPE_SYMBOLIC_LINK)
    {
        /* 避免解析软链接 */
        this->filePath = file_path.lexically_relative(std::filesystem::current_path());
    }
    else // 得到相对路径 & 化简路径
    {
        this->filePath = std::filesystem::relative(file_path).lexically_normal();
    }

    /* 填充 `FileHeader` 信息 */
    std::strncpy(this->fileHeader.filePath, this->filePath.c_str(), TAR_FILE_PATH);
    this->fileHeader.CRC = 0;
    this->fileHeader.blockNum = (this->fileHeader.metadata.st_size / TAR_RECORD_SIZE) + 1;
    std::memset(this->fileHeader.pad, 0, TAR_PAD_SIZE); // 使用 0 填充 pad 数组

    /* 创建文件读取流 */
    if (file_type & (FILE_TYPE_NORMAL | FILE_TYPE_HARD_LINK))
    {
        this->open(this->filePath, std::ios::in | std::ios::binary);
        if (!this->is_open())
        {
            std::cerr << "Open file read stream failed! - " << this->filePath << std::endl;
            return;
        }
    }
}

FileBaseUnit::FileBaseUnit(const FileHeader &file_header)
    : fileHeader(file_header), filePath(file_header.filePath)
{
    if (this->GetFileType() & (FILE_TYPE_NORMAL))
    {
        /* 打开文件输出流 */
        this->open(this->filePath, std::ios::out | std::ios::binary | std::ios::trunc);
        if (!this->is_open())
        {
            std::cerr << "File create failed! - " << this->filePath << std::endl;
            return;
        }
    }
}

FileBaseUnit::FileBaseUnit(const std::filesystem::path &file_path, BackupHeader &backup_header)
    : filePath(std::filesystem::absolute(file_path).lexically_normal()) // 转化为绝对路径
{
    /* 打开文件输出流 */
    this->open(this->filePath, std::ios::out | std::ios::binary | std::ios::trunc);
    if (!this->is_open())
    {
        std::cerr << " Package file create failed - " << file_path << std::endl;
        return;
    }

    /* 写入文件头 BackupHeader */
    backup_header.CRC = 0;
    backup_header.offset = sizeof(BackupHeader);
    this->write((const char *)(&backup_header), TAR_RECORD_SIZE);

    /* 初始化CRC校验码 */
    this->fileHeader.CRC = 0;
}

FileBaseUnit::FileBaseUnit(const std::filesystem::path &file_path, CompressHeader &compress_header)
    : filePath(std::filesystem::absolute(file_path).lexically_normal()) // 转化为绝对路径
{
    /* 打开文件输出流 */
    this->open(this->filePath, std::ios::out | std::ios::binary | std::ios::trunc);
    if (!this->is_open())
    {
        std::cerr << " Compress file create failed - " << file_path << std::endl;
        return;
    }

    /* 初始化CRC校验码 */
    this->fileHeader.CRC = 0;
}

FileBaseUnit::~FileBaseUnit()
{
    /* 关闭文件流 */
    this->close();
}

FileType FileBaseUnit::GetFileType()
{
    return this->GetFileType(this->fileHeader);
}

FileType FileBaseUnit::GetFileType(const FileHeader &file_header)
{
    switch (file_header.metadata.st_mode & S_IFMT)
    {
    /* regular file */
    case S_IFREG:
        /* hard link */
        if (file_header.metadata.st_nlink > 1)
            return FILE_TYPE_HARD_LINK;
        return FILE_TYPE_NORMAL;
    /* directory */
    case S_IFDIR:
        return FILE_TYPE_DIRECTORY;
    /* FIFO/pipe */
    case S_IFIFO:
        /* hard link */
        if (file_header.metadata.st_nlink > 1)
            return FILE_TYPE_HARD_LINK;
        return FILE_TYPE_PIPE;
#ifdef S_ISLNK
    /* symbolic link */
    case S_IFLNK:
        return FILE_TYPE_SYMBOLIC_LINK;
#endif
    /* unknown */
    default:
        /* hard link */
        if (file_header.metadata.st_nlink > 1)
            return FILE_TYPE_HARD_LINK;
        return FILE_TYPE_OTHER;
    }
}

bool FileBaseUnit::Filter(const struct FileFilterArguments &filter_arguments)
{
    /* 无需过滤 */
    if (filter_arguments.mode == 0)
        return true;

    /* 文件路径过滤 */
    if (filter_arguments.mode & FILE_FILTER_PATH)
    {
        std::regex regex_path(filter_arguments.path);
        std::filesystem::path file_path = std::filesystem::relative(filePath).lexically_normal();

        /* 非目录文件只考虑路径 */
        if (this->GetFileType() != FILE_TYPE_DIRECTORY)
            file_path = file_path.parent_path();

        if (!std::regex_search(file_path.c_str(), regex_path))
            return false;
    }

    /* 文件类型过滤 */
    if ((filter_arguments.mode & FILE_FILTER_TYPE) && !(this->GetFileType() & filter_arguments.types))
        return false;

    /* 文件名过滤 */
    if (filter_arguments.mode & FILE_FILTER_NAME)
    {
        std::regex regex_name(filter_arguments.name);
        if (!std::regex_search(filePath.filename().c_str(), regex_name))
            return false;
    }

    /* 文件大小过滤（不包含目录） */
    if ((filter_arguments.mode & FILE_FILTER_SIZE) && (this->GetFileType() != FILE_TYPE_DIRECTORY))
    {
        if (fileHeader.metadata.st_size < filter_arguments.size_from ||
            fileHeader.metadata.st_size > filter_arguments.size_to)
            return false;
    }

    /* 时间过滤 */
    if (filter_arguments.mode & FILE_FILTER_ATIME)
    {
        if (fileHeader.metadata.st_atim.tv_sec < filter_arguments.atime_from ||
            fileHeader.metadata.st_atim.tv_sec > filter_arguments.atime_to)
            return false;
    }
    if (filter_arguments.mode & FILE_FILTER_MTIME)
    {
        if (fileHeader.metadata.st_mtim.tv_sec < filter_arguments.mtime_from ||
            fileHeader.metadata.st_mtim.tv_sec > filter_arguments.mtime_to)
            return false;
    }
    if (filter_arguments.mode & FILE_FILTER_CTIME)
    {
        if (fileHeader.metadata.st_ctim.tv_sec < filter_arguments.ctime_from ||
            fileHeader.metadata.st_ctim.tv_sec > filter_arguments.ctime_to)
            return false;
    }

    return true;
}

uint32_t FileBaseUnit::Calc_CRC32(uint8_t *data, size_t length)
{
    uint8_t i;
    uint32_t crc = CRC_INIT_VALUE;
    while (length--)
    {
        crc ^= *(data++);
        for (i = 0; i < 8; ++i)
        {
            if (crc & 1)
                crc = (crc >> 1) ^ CRC_REVERSE_VALUE;
            else
                crc = (crc >> 1);
        }
    }
    return ~crc;
}

void FileBaseUnit::WriteCRC(uint32_t CRC_value, std::streampos file_head_pos)
{
    std::streampos original_pos = this->tellp();

    this->clear();
    this->seekp(file_head_pos + std::streamoff(TAR_FILE_PATH)); // 跳过文件路径
    this->write((const char *)(&CRC_value), SIZE_CRC_CODE);

    this->seekp(original_pos); // 还原文件指针
}

void FileBaseUnit::UpdateCompressHeader(const CompressHeader &compress_header)
{
    std::streampos original_pos = this->tellp();

    this->clear();
    this->seekp(std::ios::beg); // 重新覆盖
    this->write((const char *)(&compress_header), sizeof(CompressHeader));

    this->seekp(original_pos); // 还原文件指针
}

bool FileBaseUnit::ReadCompressHeader(CompressHeader &compress_header)
{
    if (!this->read((char *)(&compress_header), sizeof(CompressHeader)))
    {
        std::cerr << "Read file header failed! " << this->filePath << std::endl;
        return false;
    }

    /* CompressHeader 保证存在 */
    if (compress_header.filePath[0] == '\0' &&
        compress_header.offset != sizeof(CompressHeader))
        return false;
    return true;
}